package com.esreport.service.impl;

import com.esreport.dto.Action;
import com.esreport.entity.Report;
import com.esreport.service.ReportService;
import com.esreport.service.XReportService;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.InternalDateHistogram;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms.Bucket;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.cardinality.CardinalityAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.cardinality.InternalCardinality;
import org.elasticsearch.search.aggregations.metrics.sum.InternalSum;
import org.elasticsearch.search.aggregations.metrics.sum.SumAggregationBuilder;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class XReportServiceImpl implements XReportService {

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Autowired
    private ReportService reportService;


    @Override
    public long countBrowseNews(Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("action", "visitPage"))
                .must(QueryBuilders.matchQuery("nowScene", Action.NEWS_DETAIL))
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
        return elasticsearchTemplate.count(sq, Report.class);
    }


    @Override
    public long sumStayTime(Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        //聚合查询 停留时间求和
        SumAggregationBuilder sumBulider = AggregationBuilders.sum("sum_time").field("duration");
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(sumBulider).build();

        double saleAmount = elasticsearchTemplate.query(sq, response -> {
            InternalSum sum = (InternalSum) response.getAggregations().asList().get(0);
            return sum.getValue();
        });
        return (long) saleAmount;
    }


    @Override
    public long countShare(Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("siteId", siteId.toString()))
                .must(QueryBuilders.matchQuery("action", Action.SHARE));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
        return elasticsearchTemplate.count(sq, Report.class);
    }

    @Override
    public long countFirstStart(Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("siteId", siteId.toString()))
                .must(QueryBuilders.matchQuery("action", Action.FIRST_START));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
        return elasticsearchTemplate.count(sq, Report.class);
    }

    @Override
    public long countFirstStart(Date start, Date end, Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.rangeQuery("startTime").gte(start.getTime()).lte(end.getTime()))
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()))
                .must(QueryBuilders.matchQuery("action", Action.FIRST_START));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
        return elasticsearchTemplate.count(sq, Report.class);
    }

    @Override
    public long countStart(Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("siteId", siteId.toString()))
                .must(QueryBuilders.matchQuery("action", Action.STARTUP));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
        return elasticsearchTemplate.count(sq, Report.class);
    }

    @Override
    public long countStart(Date start, Date end, Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.rangeQuery("startTime").gte(start.getTime()).lte(end.getTime()))
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()))
                .must(QueryBuilders.matchQuery("action", Action.STARTUP));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).build();
        return elasticsearchTemplate.count(sq, Report.class);
    }

    @Override
    public long countAllUser(Integer siteId) {
        CardinalityAggregationBuilder cab = AggregationBuilders.cardinality("deviceCount").field("deviceId.keyword").precisionThreshold(Integer.MAX_VALUE);
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(cab).build();
        return elasticsearchTemplate.query(sq, response -> {
            InternalCardinality sum = (InternalCardinality) response.getAggregations().asList().get(0);
            return sum.getValue();
        });
    }

    @Override
    public long countAllUser(Date start, Date end, Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.rangeQuery("startTime").gte(start.getTime()).lte(end.getTime()))
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        CardinalityAggregationBuilder cab = AggregationBuilders.cardinality("deviceCount").field("deviceId.keyword").precisionThreshold(Integer.MAX_VALUE);
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(cab).build();
        return elasticsearchTemplate.query(sq, response -> {
            InternalCardinality sum = (InternalCardinality) response.getAggregations().asList().get(0);
            return sum.getValue();
        });
    }

    @Override
    public List<Map> countShareRank(Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        //size 查询条目数
        TermsAggregationBuilder tab = AggregationBuilders
                .terms("titlesDistinct").field("title.keyword").size(10)
                .order(BucketOrder.count(false));

        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(tab).build();
        return elasticsearchTemplate.query(sq, response -> {
            Object terms = response.getAggregations().asList().get(0);
            if (terms instanceof StringTerms) {
                StringTerms st = (StringTerms) terms;
                List<Bucket> buckets = st.getBuckets();
                if (buckets.size() > 0) {
                    List<Map> data = new LinkedList<>();
                    for (Bucket bucket : buckets) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("title", bucket.getKeyAsString());
                        map.put("count", bucket.getDocCount());
                        data.add(map);
                    }
                    return data;
                }
            }
            return null;
        });
    }

    @Override
    public List<Map> countShareRank(Date start, Date end, Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.rangeQuery("startTime").gte(start.getTime()).lte(end.getTime()))
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        //size 查询条目数
        TermsAggregationBuilder tab = AggregationBuilders
                .terms("titlesDistinct").field("title.keyword").size(10)
                .order(BucketOrder.count(false));
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(tab).build();
        return elasticsearchTemplate.query(sq, response -> {
            Object terms = response.getAggregations().asList().get(0);
            if (terms instanceof StringTerms) {
                StringTerms st = (StringTerms) terms;
                List<Bucket> buckets = st.getBuckets();
                if (buckets.size() > 0) {
                    List<Map> data = new LinkedList<>();
                    for (Bucket bucket : buckets) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("title", bucket.getKeyAsString());
                        map.put("count", bucket.getDocCount());
                        data.add(map);
                    }
                    return data;
                }
            }
            return null;
        });
    }

    @Override
    public long countAvgStayTime(Integer siteId) {
        long count = this.countSeriesSign(siteId);
        long time = this.sumStayTime(siteId);
        return time / count;
    }

    @Override
    public long countAvgStayTime(Date start, Date end, Integer siteId) {
        long count = this.countSeriesSign(start, end, siteId);
        long time = reportService.sumStayTime(start, end, siteId);
        return count == 0 ? 0 : time / count;
    }


    public long countSeriesSign(Date start, Date end, Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.rangeQuery("startTime").gte(start.getTime()).lte(end.getTime()))
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        CardinalityAggregationBuilder cab = AggregationBuilders.cardinality("count").field("seriesSign").precisionThreshold(Integer.MAX_VALUE);
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(cab).build();
        return elasticsearchTemplate.query(sq, response -> {
            InternalCardinality sum = (InternalCardinality) response.getAggregations().asList().get(0);
            return sum.getValue();
        });
    }

    public long countSeriesSign(Integer siteId) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        CardinalityAggregationBuilder cab = AggregationBuilders.cardinality("count").field("seriesSign").precisionThreshold(Integer.MAX_VALUE);
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(cab).build();
        return elasticsearchTemplate.query(sq, response -> {
            InternalCardinality sum = (InternalCardinality) response.getAggregations().asList().get(0);
            return sum.getValue();
        });
    }

    @Override
    public List<Map> granularQuery(Date start, Date end, Integer siteId, DateHistogramInterval type) {
        QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(QueryBuilders.matchQuery("action", "visitPage"))
                .must(QueryBuilders.matchQuery("nowScene", Action.NEWS_DETAIL))
                .must(QueryBuilders.rangeQuery("startTime").gte(start.getTime()).lte(end.getTime()))
                .must(QueryBuilders.matchQuery("siteId", siteId.toString()));
        DateHistogramAggregationBuilder dateAgg = AggregationBuilders.dateHistogram("by_time").field("startTime").timeZone(DateTimeZone.forOffsetHours(8)).dateHistogramInterval(type);
        SearchQuery sq = new NativeSearchQueryBuilder().withQuery(queryBuilder).addAggregation(dateAgg).build();
        return elasticsearchTemplate.query(sq, response -> {
            InternalDateHistogram sum = (InternalDateHistogram) response.getAggregations().asList().get(0);
            List<InternalDateHistogram.Bucket> buckets = sum.getBuckets();
            if (buckets.size() > 0) {
                List<Map> data = new LinkedList<>();
                for (InternalDateHistogram.Bucket bucket : buckets) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("date", ((DateTime) bucket.getKey()).getMillis());
                    map.put("count", bucket.getDocCount());
                    data.add(map);
                }
                return data;
            }
            return null;
        });
    }
}